#!/usr/bin/env python3
"""
Windows 下智能剪切板打包脚本
使用 PyInstaller 将应用程序打包成可执行文件
"""

import os
import sys
import shutil
import subprocess
from pathlib import Path


def install_dependencies():
    """安装项目依赖"""
    print("正在安装项目依赖...")
    
    # 直接使用 pip 安装到当前 Python 环境
    # 这样确保依赖安装到脚本正在运行的 Python 环境中
    try:
        print("使用 pip 安装 PySide6...")
        subprocess.check_call([sys.executable, "-m", "pip", "install", "PySide6>=6.6.0"], 
                            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        print("✓ PySide6 安装完成")
        
        print("使用 pip 安装 Pillow...")
        subprocess.check_call([sys.executable, "-m", "pip", "install", "Pillow>=10.0.0"],
                            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        print("✓ Pillow 安装完成")
        
        print("✓ 所有依赖安装完成")
        return True
    except subprocess.CalledProcessError as e:
        print(f"✗ 依赖安装失败: {e}")
        # 如果 pip 失败，尝试使用 uv 但是安装到系统环境
        try:
            print("尝试使用 uv 安装到系统环境...")
            subprocess.check_call(["uv", "pip", "install", "PySide6>=6.6.0"], 
                                stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            subprocess.check_call(["uv", "pip", "install", "Pillow>=10.0.0"],
                                stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            print("✓ uv pip 安装完成")
            return True
        except (subprocess.CalledProcessError, FileNotFoundError):
            print("✗ uv pip 也失败了")
            return False


def check_requirements():
    """检查打包所需的依赖"""
    print("检查打包环境...")
    
    # 检查 PyInstaller
    try:
        import PyInstaller
        print(f"✓ PyInstaller 已安装: {PyInstaller.__version__}")
    except ImportError:
        print("✗ PyInstaller 未安装，正在安装...")
        subprocess.check_call([sys.executable, "-m", "pip", "install", "pyinstaller"])
        print("✓ PyInstaller 安装完成")
    
    # 检查项目依赖
    dependencies_missing = False
    
    try:
        import PySide6
        print(f"✓ PySide6 已安装: {PySide6.__version__}")
    except ImportError:
        print("✗ PySide6 未安装")
        dependencies_missing = True
    
    try:
        import PIL
        print(f"✓ Pillow 已安装: {PIL.__version__}")
    except ImportError:
        print("✗ Pillow 未安装")
        dependencies_missing = True
    
    # 如果有依赖缺失，尝试安装
    if dependencies_missing:
        print("\n正在自动安装缺失的依赖...")
        if not install_dependencies():
            return False
        
        # 重新检查依赖 - 需要重启 Python 进程才能加载新安装的包
        print("\n重新验证依赖安装...")
        
        # 使用子进程来检查依赖是否真的安装成功
        try:
            result = subprocess.run([sys.executable, "-c", "import PySide6; print('PySide6:', PySide6.__version__)"], 
                                  capture_output=True, text=True, timeout=10)
            if result.returncode == 0:
                print("✓", result.stdout.strip())
            else:
                print("✗ PySide6 验证失败:", result.stderr.strip())
                return False
                
            result = subprocess.run([sys.executable, "-c", "import PIL; print('Pillow:', PIL.__version__)"], 
                                  capture_output=True, text=True, timeout=10)
            if result.returncode == 0:
                print("✓", result.stdout.strip())
            else:
                print("✗ Pillow 验证失败:", result.stderr.strip())
                return False
                
            print("✓ 所有依赖验证成功")
            
        except subprocess.TimeoutExpired:
            print("✗ 依赖验证超时")
            return False
        except Exception as e:
            print(f"✗ 依赖验证异常: {e}")
            return False
    
    return True


def clean_build():
    """清理之前的构建文件"""
    print("清理构建目录...")
    
    dirs_to_clean = ["build", "dist", "__pycache__"]
    for dir_name in dirs_to_clean:
        if os.path.exists(dir_name):
            shutil.rmtree(dir_name)
            print(f"✓ 已清理 {dir_name}")
    
    # 清理 .spec 文件
    spec_files = list(Path(".").glob("*.spec"))
    for spec_file in spec_files:
        spec_file.unlink()
        print(f"✓ 已清理 {spec_file}")


def create_icon():
    """创建应用图标（如果不存在）"""
    icon_path = Path("assets/icon.ico")
    
    if not icon_path.exists():
        print("未找到图标文件，创建默认图标...")
        # 创建 assets 目录
        icon_path.parent.mkdir(exist_ok=True)
        
        # 这里可以放置一个默认图标，或者从网上下载一个
        # 暂时返回 None，PyInstaller 会使用默认图标
        return None
    
    return str(icon_path)


def build_exe():
    """构建可执行文件"""
    print("开始构建可执行文件...")
    
    # 生成版本信息文件
    from version_info import create_version_info
    create_version_info()
    
    # 检查是否使用spec文件
    spec_file = Path("SmartClipboard.spec")
    if spec_file.exists():
        print("使用spec文件进行构建...")
        build_args = ["pyinstaller", "--clean", str(spec_file)]
    else:
        print("使用命令行参数进行构建...")
        # 构建参数
        build_args = [
            "pyinstaller",
            "--onefile",  # 单文件模式
            "--windowed",  # 窗口模式（不显示控制台）
            "--name=SmartClipboard",  # 可执行文件名
            "--distpath=dist",  # 输出目录
            "--workpath=build",  # 工作目录
            "--specpath=.",  # spec文件路径
            
            # 添加数据文件和隐藏导入
            "--hidden-import=PySide6.QtCore",
            "--hidden-import=PySide6.QtWidgets", 
            "--hidden-import=PySide6.QtGui",
            "--hidden-import=PIL",
            "--hidden-import=sqlite3",
            
            # 添加源码路径
            "--add-data=src;src",
            
            # 添加版本信息
            "--version-file=version_info.txt",
            
            # 启动脚本
            "run.py"
        ]
        
        # 添加图标（如果存在）
        icon_path = create_icon()
        if icon_path:
            build_args.extend(["--icon", icon_path])
    
    # 执行构建
    try:
        subprocess.check_call(build_args)
        print("✓ 构建完成！")
        return True
    except subprocess.CalledProcessError as e:
        print(f"✗ 构建失败: {e}")
        return False


def create_installer_script():
    """创建安装脚本"""
    print("创建安装脚本...")
    
    installer_script = '''@echo off
echo 智能剪切板安装程序
echo.

set "INSTALL_DIR=%LOCALAPPDATA%\\SmartClipboard"
set "DESKTOP_LINK=%USERPROFILE%\\Desktop\\智能剪切板.lnk"
set "STARTMENU_LINK=%APPDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\智能剪切板.lnk"

echo 创建安装目录...
if not exist "%INSTALL_DIR%" mkdir "%INSTALL_DIR%"

echo 复制文件...
copy "SmartClipboard.exe" "%INSTALL_DIR%\\SmartClipboard.exe"

echo 创建桌面快捷方式...
powershell "$WshShell = New-Object -comObject WScript.Shell; $Shortcut = $WshShell.CreateShortcut('%DESKTOP_LINK%'); $Shortcut.TargetPath = '%INSTALL_DIR%\\SmartClipboard.exe'; $Shortcut.WorkingDirectory = '%INSTALL_DIR%'; $Shortcut.Description = '智能剪切板'; $Shortcut.Save()"

echo 创建开始菜单快捷方式...
powershell "$WshShell = New-Object -comObject WScript.Shell; $Shortcut = $WshShell.CreateShortcut('%STARTMENU_LINK%'); $Shortcut.TargetPath = '%INSTALL_DIR%\\SmartClipboard.exe'; $Shortcut.WorkingDirectory = '%INSTALL_DIR%'; $Shortcut.Description = '智能剪切板'; $Shortcut.Save()"

echo.
echo 安装完成！
echo 程序已安装到: %INSTALL_DIR%
echo 桌面和开始菜单已创建快捷方式
echo.
pause
'''
    
    with open("dist/install.bat", "w", encoding="gbk") as f:
        f.write(installer_script)
    
    print("✓ 安装脚本创建完成: dist/install.bat")


def create_uninstaller_script():
    """创建卸载脚本"""
    print("创建卸载脚本...")
    
    uninstaller_script = '''@echo off
echo 智能剪切板卸载程序
echo.

set "INSTALL_DIR=%LOCALAPPDATA%\\SmartClipboard"
set "DESKTOP_LINK=%USERPROFILE%\\Desktop\\智能剪切板.lnk"
set "STARTMENU_LINK=%APPDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\智能剪切板.lnk"

echo 正在卸载智能剪切板...

echo 删除桌面快捷方式...
if exist "%DESKTOP_LINK%" del "%DESKTOP_LINK%"

echo 删除开始菜单快捷方式...
if exist "%STARTMENU_LINK%" del "%STARTMENU_LINK%"

echo 删除程序文件...
if exist "%INSTALL_DIR%" (
    rmdir /s /q "%INSTALL_DIR%"
)

echo.
echo 卸载完成！
echo.
pause
'''
    
    with open("dist/uninstall.bat", "w", encoding="gbk") as f:
        f.write(uninstaller_script)
    
    print("✓ 卸载脚本创建完成: dist/uninstall.bat")


def create_readme():
    """创建使用说明"""
    print("创建使用说明...")
    
    readme_content = '''智能剪切板 - 使用说明
======================

安装说明:
1. 双击 install.bat 进行安装
2. 程序将安装到 %LOCALAPPDATA%\\SmartClipboard 目录
3. 桌面和开始菜单将创建快捷方式

使用说明:
1. 双击桌面快捷方式或从开始菜单启动程序
2. 程序启动后会最小化到系统托盘
3. 双击托盘图标可以打开主界面
4. 右键托盘图标可以访问菜单

功能特性:
- 自动监控剪切板变化
- 支持文本、图片等多种格式
- 提供搜索功能
- 支持数据持久化存储

卸载说明:
双击 uninstall.bat 进行卸载

系统要求:
- Windows 10 或更高版本
- .NET Framework 4.7.2 或更高版本

技术支持:
如有问题请联系开发团队

版本: 1.0.0
'''
    
    with open("dist/README.txt", "w", encoding="utf-8") as f:
        f.write(readme_content)
    
    print("✓ 使用说明创建完成: dist/README.txt")


def create_package():
    """创建最终的打包文件"""
    print("创建发布包...")
    
    # 检查是否有7zip或WinRAR来创建压缩包
    zip_tools = ["7z", "winrar"]
    zip_tool = None
    
    for tool in zip_tools:
        if shutil.which(tool):
            zip_tool = tool
            break
    
    if zip_tool == "7z":
        # 使用7zip创建压缩包
        package_name = "SmartClipboard_v1.0.0_Windows.7z"
        cmd = [
            "7z", "a", "-t7z", package_name,
            "dist/SmartClipboard.exe",
            "dist/install.bat", 
            "dist/uninstall.bat",
            "dist/README.txt"
        ]
        try:
            subprocess.check_call(cmd)
            print(f"✓ 发布包创建完成: {package_name}")
        except subprocess.CalledProcessError:
            print("✗ 7zip 压缩失败")
    else:
        print("未找到压缩工具，请手动打包 dist 目录中的文件")
        print("建议安装 7-Zip 以自动创建发布包")


def main():
    """主函数"""
    print("=" * 50)
    print("智能剪切板 Windows 打包脚本")
    print("=" * 50)
    
    # 检查环境
    if not check_requirements():
        print("环境检查失败，请先安装所需依赖")
        return 1
    
    # 清理构建目录
    clean_build()
    
    # 构建可执行文件
    if not build_exe():
        print("构建失败，请检查错误信息")
        return 1
    
    # 创建安装脚本
    create_installer_script()
    create_uninstaller_script()
    create_readme()
    
    # 创建发布包
    create_package()
    
    print("\n" + "=" * 50)
    print("打包完成！")
    print("=" * 50)
    print(f"可执行文件: dist/SmartClipboard.exe")
    print(f"安装脚本: dist/install.bat")
    print(f"卸载脚本: dist/uninstall.bat") 
    print(f"使用说明: dist/README.txt")
    print("\n发布时请将 dist 目录中的所有文件一起打包")
    
    return 0


if __name__ == "__main__":
    sys.exit(main()) 